<!DOCTYPE html>
<html lang="en">
  <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="author" content="Zhou Wei <zromyk@163.com>">
  <title>深入理解C++11新特性</title>
  <link rel="shortcut icon" href="/favicon.ico">
  <link rel="stylesheet" href="/style/pure.css">
  <link rel="stylesheet" href="/style/main.css">
  <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.css">
  <link href="https://apps.bdimg.com/libs/highlight.js/9.1.0/styles/default.min.css" rel="stylesheet">
  <script src='/style/baidu.js'></script>
</head>
<body>
  <div id="menu-background"></div>
  <div id="menu">
    <div class="pure-menu pure-menu-horizontal">
  <div id="menu-block">
    <ul class="pure-menu-list">
      <a class="pure-menu-heading" href="/index.html">ZROMYK</a>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/index.html">主页</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/archive/index.html">归档</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/download/index.html">下载</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/feedback/index.html">反馈</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/about/index.html">关于我</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="https://github.com/zromyk"><i class="fa fa-github" style="font-size:32px"></i></a>
</li>

    </ul>
  </div>
</div>

  </div>
  <div id="layout">
    <div class="content">
      <div id="content-articles">
  <h1 id="深入理解C++11新特性" class="content-subhead">深入理解C++11新特性</h1>
  <p>
    <span>2021-04-11</span>
    <span><span class="post-category post-category-cpp">C++</span></span>
    <span id="/public/article/深入理解C++11新特性.html" class="leancloud_visitors" style="display:none" data-flag-title="深入理解C++11新特性"></span>
  </p>
  <blockquote class="content-quote">
<p>本文所有程序测试平台：</p>
<p>MacBook Pro (16-inch, 2019)</p>
<p>2.6 GHz 六核Intel Core i7</p>
<p>16 GB 2667 MHz DDR4</p>
<p>Intel UHD Graphics 630 1536 MB</p>
<p>编译器 g++</p>
</blockquote>
<h2 id="_1">第二章</h2>
<h3 id="21-c99">2.1 与 C99 兼容</h3>
<h4 id="211">2.1.1 预定义宏</h4>
<div class="pure-table-scrollable"><table class="pure-table pure-table-horizontal">
<thead>
<tr>
<th>宏名称</th>
<th>功能描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><span style="overflow-x: auto; max-width:100%; display:inline;"><code>__STDC_HOSTED__</code></span></td>
<td>如果编译器的目标系统环境或中包含完整的标准 C 库，name 整个宏定义就为 1，否则为 0</td>
</tr>
<tr>
<td><span style="overflow-x: auto; max-width:100%; display:inline;"><code>__STDC__</code></span></td>
<td>C 编译器通常用这个宏的值来表示编译器的实现是否和 C 标准一致。<br />C++11 标准中这个宏是否定义以及定成什么值由编译器来决定。</td>
</tr>
<tr>
<td><span style="overflow-x: auto; max-width:100%; display:inline;"><code>__STDC_VERSION__</code></span></td>
<td>C 编译器通常用这个宏来表示所支持的 C 标准的版本，比如 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>1999mml</code></span>。<br />C++11 标准中这个宏是否定义以及定成什么值由编译器来决定。</td>
</tr>
<tr>
<td><span style="overflow-x: auto; max-width:100%; display:inline;"><code>__STDC_ISO_10646__</code></span></td>
<td>这个宏通常定义为一个 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>yyymmL</code></span> 格式的整形常量，如 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>199712L</code></span>，用来表示 C++ 编译器符合某个版本的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>ISO/IEC 10646</code></span> 标准</td>
</tr>
</tbody>
</table></div>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#include &lt;iostream&gt;
using namespace std;

int main() {
    cout &lt;&lt; &quot;Standard Clib:      &quot; &lt;&lt; __STDC_HOSTED__    &lt;&lt; endl;   // 1
    cout &lt;&lt; &quot;Standard C:         &quot; &lt;&lt; __STDC__           &lt;&lt; endl;   // 1
    // cout &lt;&lt; &quot;C Standard version: &quot; &lt;&lt; __STDC_VERSION__   &lt;&lt; endl; // 编译器未定义
    // cout &lt;&lt; &quot;ISO/IEC:            &quot; &lt;&lt; __STDC_ISO_10646__ &lt;&lt; endl; // 编译器未定义
    return 0;
}
// g++ -std=c++11 2-1-1.cpp
</code></span></code></pre>
<h4 id="212-__func__">2.1.2 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>__func__</code></span> 预定义标识符</h4>
<p>功能：返回其所在函数的名字。</p>
<p>注意：<span style="overflow-x: auto; max-width:100%; display:inline;"><code>__func__</code></span> 不能作为函数的形参，因为在声明参数时，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>__func__</code></span> 还未定义。</p>
<h4 id="213-_pragma">2.1.3 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>_Pragma</code></span> 操作符</h4>
<p>在 C/C++ 标准中，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>#Pragma</code></span> 是一条预处理指令，用来向编译器传达语言标准以外的信息。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#pragma once
</code></span></code></pre>
<p>该指令指示编译器，该头文件应该只被编译一次。</p>
<p>在 C++11 中，标准定义了与预处理指令 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>#pragma</code></span> 功能相同的操作符 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>_Pragma</code></span>：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">_Pragma (字符串字面值)
_Pragma (&quot;once&quot;) // 可以达到与 #pragma once 相同的效果
</code></span></code></pre>
<p>与预处理指令 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>#pragma</code></span> 相比，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>_Pragma</code></span> 是一个操作符，可以用在一些宏中。而且，C/C++ 规定 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>#预处理指令</code></span> 必须是该行的第一个非空字符，因此作用有限。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#define CONCAT(x) PRAGMA(concat on #x)
#define PRAGMA(x) _Pragma(#x)
CONCAT( ..\concat.dir ) 
// 最终效果 _Pragma(concat on &quot;..\concat.dir&quot;)
// 这里只显示效果，应该没有编译器会支持 展开后的语法
</code></span></code></pre>
<h4 id="214-__va_args__">2.1.4 变长参数的宏定义以及 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>__VA_ARGS__</code></span></h4>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#include &lt;stdio.h&gt;

#define LOG(...) {\
    fprintf(stderr, &quot;%s: Line %d:\t&quot;, __FILE__, __LINE__);\
    fprintf(stderr, __VA_ARGS__);\
    fprintf(stderr, &quot;\n&quot;);\
}

int main() {
    int x = 3;
    // 一些代码 ...
    LOG(&quot;x = %d&quot;, x); // /.../1-4.cpp: Line 12: x = 3
    return 0;
}
// g++ -std=c++11 2-1-4.cpp
</code></span></code></pre>
<h3 id="24-__cplusplus">2.4 宏 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>__cplusplus</code></span></h3>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#ifdef __cplusplus
extern &quot;C&quot; {
#endif
// 一些代码
#ifdef __cplusplus
}
#endif
</code></span></code></pre>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>__cplusplus</code></span> 通常被定义为一个整型值，一般会随着标准更新而增大。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#define __cplusplus 199711L // C++03
#define __cplusplus 201103L // C++11
</code></span></code></pre>
<p>程序员想在确定代码是使用支持 C++11 编译器进行编译时，可以使用以下方法检测：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#if __cplusplus &lt; 201103L
    #errer &quot;shoule use C++11 implementation&quot;
#endif
</code></span></code></pre>
<p>这里，使用了预处理命令 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>#error</code></span>，使得不支持 C++11 的代码编译会立即报错并终止编译。</p>
<h3 id="25">2.5 静态断言</h3>
<h4 id="251">2.5.1 动态断言</h4>
<p>C++ 标准在 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>&lt;cassert&gt;</code></span> 或 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>&lt;assert.h&gt;</code></span> 头文件中为程序员提供 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>assert</code></span> 宏，用于在 <strong>运行时</strong> 进行断言。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#include &lt;cassert&gt;

using namespace std;

char * ArrayAlloc(int n) {
    assert(n &gt; 0); // 断言，n 必须大于 0，后期 debug 时可以设置程序在此死循环
    return new char[n];
}

int main() {
    char *a = ArrayAlloc(0);
    return 0;
}
// g++ 2-5-1.cpp
</code></span></code></pre>
<p>运行结果：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-shell">Assertion failed: (n &gt; 0), function ArrayAlloc, file /.../1-6.cpp, line 6.
</code></span></code></pre>
<p>使用宏定义 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>NDEBUG</code></span> 可以禁用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>assert</code></span> 宏：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#ifdef NDEBUG
    #define assert(expr)        (static_cast&lt;void&gt; (0))
#else
...
#endif /* NDEBUG */
</code></span></code></pre>
<p>一旦定义了 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>NDEBUG</code></span> 宏，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>assert</code></span> 宏将被展开为一条无意义的 C 语句（通常会被编译器优化掉）。</p>
<h4 id="252-static_assert">2.5.2 静态断言与 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>static_assert</code></span></h4>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>assert</code></span> 宏只有在程序运行时才能起作用。而 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>#error</code></span> 只有在编译器预处理时才起作用。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">/*
例子：使用断言来检查枚举重位
*/
#include &lt;cassert&gt;

using namespace std;

// 枚举编译器对各种特性的支持
enum FeatureSupports {
    C99         = 0x0001,
    ExtInt      = 0x0002,
    SAssert     = 0x0004,
    NoExcept    = 0x0008,
    SMAX        = 0x0010
}

// 一个编译器类型，包括名称、特性支持等
struct Compiler {
    const char * name;
    int spp; // 使用 FeatureSupports 枚举
};

int main() {
    // 检查枚举值是否完备
    assert((SMAX - 1) == (C99 | ExtInt | SAssert | NoExcept));
    // 若 C99 | ExtInt | SAssert | NoExcept 均未重位，则结果为 0x000F = 0x0010 - 1
    Compiler a = {&quot;abc&quot;, (C99 | SAssert)};
    // ...
    if (a.spp &amp; C99) {
        // ...
    }
}
// g++ 2-5-2-1.cpp
</code></span></code></pre>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>assert</code></span> 是一个运行时的断言，这意味着 <strong>不运行程序我们无法进行断言</strong>，在一些情况下，单次运行并不会调用到 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>assert</code></span> 相关的代码。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">/*
使用断言来检查形参的大小是否相等
*/
#include &lt;cassert&gt;
#include &lt;cstring&gt;

using namespace std;

template &lt;typename T, typename U&gt;
int bit_copy(T &amp;a, U &amp;b) {
    assert(sizeof(a), == sizeof(b));
    memcpy(&amp;a, &amp;b, sizeof(a));
}

int main() {
    int a;
    double b;
    bit_copy(a, b); // 如果未调用 bit_copy 我们无法知道该函数断言是否正确
}
// g++ 2-5-2-2.cpp
</code></span></code></pre>
<p>利用语言规则实现静态断言的讨论非常多，比较典型的是 <strong>开源库 Boost 内置的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>BOOST_STATIC_ASSERT</code></span> 断言机制</strong>（利用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>sizeof</code></span> 操作符，除以 0 会导致编译错误这个特性来实现静态断言）</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#define assert_static(e) \
    do {
        enum { assert_static__ = 1 / (e)}; \
    } while(0);
</code></span></code></pre>
<p>在 C++11 标准中，引入了 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>static_assert</code></span> 断言来解决这个问题。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">static_assert(e, &quot;error message&quot;)
    // static_assert 有两个参数，一个是断言表达式，另一个是警告信息
</code></span></code></pre>
<p>此外，静态断言语句可以写在函数体外，方便代码阅读者发现断言。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">static_assert(sizeof(int) == 8, &quot;This 64-bit machine should follow this!&quot;);
int main() {
    return 0;
}
</code></span></code></pre>
<p>静态断言中的断言表达式必须为常量表达式，且在编译时期可以计算，如果含有变量，会导致编译出错。</p>
<h3 id="26-noexcapt-noexcept">2.6 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>noexcapt</code></span> 修饰符与 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>noexcept</code></span> 操作符</h3>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void excpt_func() noexcept;
void excpt_func() noexcept (常量表达式);
// 常量表达式 的结果被转换成一个 bool 类型
// true 表示该函数不会抛出异常
// fasle 表示有可能抛出异常
</code></span></code></pre>
<p>C++98</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">template&lt;class T&gt; class A {
public:
    static constexpr T min() throw() { return T(); }
    static constexpr T max() throw() { return T(); }
} // throw() 表示该函数不会抛出异常

void * operator new(std::size_t) throw(std::bad_alloc);
void * operator new[](std::size_t) throw(std::bad_alloc);
// throw(std::bad_alloc) 该方法接收 std::bad_alloc 异常
</code></span></code></pre>
<p>C++11</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">template&lt;class T&gt; class A {
public:
    static constexpr T min() noexcept { return T(); }
    static constexpr T max() noexcept { return T(); }
} // noexcept 表示该函数不会抛出异常

void * operator new(std::size_t) noexcept(false);
void * operator new[](std::size_t) noexcept(false);
// noexcept(false) 该方法可能抛出异常
</code></span></code></pre>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>noexcept</code></span> 更大的用处是保证应用程序的安全，比如保证一个析构函数不应该抛出异常，那么对于常被析构函数调用的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>delete</code></span> 函数来说，C++11 默认将 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>delete</code></span> 函数设置成 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>noexcept</code></span>，就可以提高应用程序的安全性。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void operator delete(void *) noexcept;
void operator delete[](void *) noexcept;
</code></span></code></pre>
<h3 id="27">2.7 快速初始化成员变量</h3>
<p>C++98 中，支持在类声明的时候使用等号&rdquo;=&rdquo;初始化类中 <strong>静态成员变量</strong>，这种声明方式我们称之为“就地”声明。</p>
<p>但是，C++98要求 <strong>静态成员</strong> 必须满足 <strong>常量性</strong>，而且类型必须是 <strong>整型或者枚举型</strong>，而非静态成员变量的初始化则必须在构造函数中进行。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class Init
{
public:
    Init() : a(0) {}
    Init(int d) : a(d) {}
private:
    int a;
    const static int          b = 0;
                 int          c = 0;    // 成员，无法通过编译
          static int          d = 0;    // 成员，无法通过编译
    const static double       e = 3.14; // 非整型或者枚举型，无法通过编译
    const static char * const f = &quot;e&quot;;  // 非整型或者枚举型，无法通过编译
};
</code></span></code></pre>
<p>C++11 中，允许使用 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>等号=</code></span> 或者 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>花括号{}</code></span> 进行就地对 <strong>非静态成员变量</strong> 初始化。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class Init
{
public:
    Init(int i, int j) : c(i), d(j) {}
private:
    int c; 
    int d;
};

class InitStruct
{
private:
    int     num = 1;
    string  str{ &quot;hello&quot; }; 
    Init    init{ 1, 3 };
};
// 使用等号或花括号进行就地初始化
</code></span></code></pre>
<h3 id="28-sizeof">2.8 非静态成员的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>sizeof</code></span></h3>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">#include &lt;iostream&gt;

using namespace std;

struct People {
public:
    int hand;
    static People * all;
};

int main() {
    People p;
    cout &lt;&lt; sizeof(P.head) &lt;&lt; endl;       // C++98 通过，C++11 通过
    cout &lt;&lt; sizeof(People::all) &lt;&lt; endl;  // C++98 通过，C++11 通过
    cout &lt;&lt; sizeof(People::head) &lt;&lt; endl; // C++98 【错误】，C++11 通过
    return 0;
}
// g++ 2-8-1.cpp
</code></span></code></pre>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// sizeof( ((People *)0)-&gt;head ); // C++98 中的临时用法
sizeof(People::hand); // C++11
</code></span></code></pre>
<h3 id="29-friend">2.9 扩展的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>friend</code></span> 语法</h3>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class Poly;
typedef Poly P;

class LiLei     { friend class Poly; }; // C++98 通过，C++11通过
class Jim       { friend       Poly; }; // C++98 失败，C++11通过
class HanMeiMei { friend       P; };    // C++98 失败，C++11通过
</code></span></code></pre>
<p>C++11 中还可以动态确定一个类的友元：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class P;

template &lt;template T&gt; 
class People {
    friend T;
}

People&lt;P&gt;   PP; // 类 P 是 People 类的友元
People&lt;int&gt; Pi; // 对于内置 int 类型模版参数，友元声明被忽略
</code></span></code></pre>
<p>该扩展可以应用在单元测试中：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">template &lt;template T&gt; 
class DefenderT { friend T; };

// 普通类的定义，使用 int 作参数
using Defender = DefenderT&lt;int&gt;;

#ifdef UNIT_TEST
class Validator { 
public:
    void Validata(DefenderTest &amp;d) {}
}
// 测试专用的定义，Validator 成为 DefenderT 的友元
using DefenderTest = DefenderT&lt;Validator&gt;; 

int main() {
    DefenderTest d;
    Validator v;

    v.Validata(d);
    return 0;
}
#endif
// g++ -std=c++11 2-9.cpp -UNIT_TEST
</code></span></code></pre>
<h3 id="210-finaloverride">2.10 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>final/override</code></span> 控制</h3>
<h4 id="final"><span style="overflow-x: auto; max-width:100%; display:inline;"><code>final</code></span></h4>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>final</code></span> 用来终止虚函数在派生类中被重写。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class Object                  { public: virtual void fun() = 0;   };
class Base    : public Object { public:         void fun() final; }; // 不希望在派生类中被重写
class Derived : public Base   { public:         void fun();       }; // 编译错误
// g++ -std=c++11 2-10.cpp
</code></span></code></pre>
<p>另外，<span style="overflow-x: auto; max-width:100%; display:inline;"><code>final</code></span> 可以用来指出基类不可以被继承。</p>
<h4 id="override"><span style="overflow-x: auto; max-width:100%; display:inline;"><code>override</code></span></h4>
<p><span style="overflow-x: auto; max-width:100%; display:inline;"><code>override</code></span> 用来显式指出派生类中必须重写基类中的虚函数。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">class Base {
public:
    virtual void Turing() = 0;
    virtual void Dijkstra() = 0;
    virtual void VNeumann(int g) = 0;
    virtual void DKnuth() const;
    void Print();
};

class DerivedMid : public Base {
    void VNeumann(double g);
}

class DerivedTop : public DerivedMid {
public:
    void Turing() override;
    void Dikjstra() override;           // 编译错误，_ijk_ 和 _ikj_ 拼写错误，并非重写
    // void VNeumann(int g) override;   
    void VNeumann(double g) override;   // 编译错误，参数不一致，非虚函数重写
    void DKnuth() override;             // 编译错误，常量性不一致，非虚函数重写
    void Print() override;              // 编译错误，非虚函数重写
};
// g++ -std=c++11 2-10.cpp
</code></span></code></pre>
<h3 id="211_1">2.11 模版函数的默认模版参数</h3>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">void DefParm(int m = 3) {}  // c++98 编译通过，c++11 编译通过

template &lt;typename T = int&gt; // c++98 编译通过，c++11 编译通过
class DefClass {};

template &lt;typename T = int&gt; // c++98 编译失败，c++11 编译通过
void DefTempParm() {};
// g++ -std=c++11 2-11-1.cpp
</code></span></code></pre>
<p>与类模版不同，在为多个默认模版参数声明制定默认值时，程序员必须遵照“从右往左”的规则进行指定。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">   template &lt;typename T1,       typename T2 = int&gt; class DefClass1;
// template &lt;typename T1 = int, typename T2      &gt; class DefClass2; // 无法编译通过

   template &lt;typename T,        int i = 0 &gt; class DefClass3;
// template &lt;int i = 0,         typename T&gt; class DefClass4; // 无法编译通过
// 不按从右往左定义默认类模版参数的模版类都无法通过编译

template &lt;typename T1 = int, typename T2&gt; void DefFunc1(T1 a, T2 b);
template &lt;int i = 0,         typename T&gt;  void DefFunc2(T a);
// g++ -std=c++11 2-11-2.cpp
</code></span></code></pre>
<h3 id="212">2.12 外部模版</h3>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// test.h
template &lt;typename T&gt;
void fun(T) {}

// test1.cpp
#include &quot;test.h&quot; //       一份 fun&lt;int&gt;(int)
void test1() { fun(3); }

// test2.cpp
#include &quot;test.h&quot; // 【另】 一份 fun&lt;int&gt;(int)
void test2() { fun(4); }
</code></span></code></pre>
<p>外部模版的使用方法：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// test.h
template &lt;typename T&gt;
void fun(T) {}

// test1.cpp
#include &quot;test.h&quot;
template void fun&lt;int&gt;(int); // 显式实例化，只有一份 fun&lt;int&gt;(int)
void test1() { fun(3); }

// test2.cpp
#include &quot;test.h&quot;
extern template void fun&lt;int&gt;(int);
void test2() { fun(4); }
</code></span></code></pre>
<h3 id="213">2.13 局部和匿名类型作模版实参</h3>
<h2 id="_2">第三章</h2>
<h3 id="31">3.1 继承构造函数</h3>
<h3 id="32">3.2 委托构造函数</h3>
</div>
<div id="nav">
  <div class="navigation">
  <ul class="pure-menu-list">
    <li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 0.75em;" href="#_1">第二章</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#21-c99">2.1 与 C99 兼容</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.75em;" href="#211">2.1.1 预定义宏</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.75em;" href="#212-__func__">2.1.2 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>__func__</code></span> 预定义标识符</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.75em;" href="#213-_pragma">2.1.3 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>_Pragma</code></span> 操作符</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.75em;" href="#214-__va_args__">2.1.4 变长参数的宏定义以及 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>__VA_ARGS__</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#24-__cplusplus">2.4 宏 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>__cplusplus</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#25">2.5 静态断言</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.75em;" href="#251">2.5.1 动态断言</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.75em;" href="#252-static_assert">2.5.2 静态断言与 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>static_assert</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#26-noexcapt-noexcept">2.6 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>noexcapt</code></span> 修饰符与 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>noexcept</code></span> 操作符</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#27">2.7 快速初始化成员变量</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#28-sizeof">2.8 非静态成员的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>sizeof</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#29-friend">2.9 扩展的 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>friend</code></span> 语法</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#210-finaloverride">2.10 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>final/override</code></span> 控制</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.75em;" href="#final"><span style="overflow-x: auto; max-width:100%; display:inline;"><code>final</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.75em;" href="#override"><span style="overflow-x: auto; max-width:100%; display:inline;"><code>override</code></span></a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#211_1">2.11 模版函数的默认模版参数</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#212">2.12 外部模版</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#213">2.13 局部和匿名类型作模版实参</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 0.75em;" href="#_2">第三章</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#31">3.1 继承构造函数</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" style="padding:0.1em 0em 0.1em 1.25em;" href="#32">3.2 委托构造函数</a>
</li>

  </ul>
</div>

</div>
 
    </div>
  </div>
  <div id="footer">
    <div class="legal pure-g">
  <div class="pure-u-1 u-sm-1-2">
    <p class="legal-license"><a href="https://beian.miit.gov.cn/#/Integrated/index">浙ICP备2020038748号</a></p>
  </div>
  <div class="pure-u-1 u-sm-1-2">
    <p class="legal-links"><a href="https://github.com/zromyk">GitHub</a></p>
    <p class="legal-copyright">Copyright © 2021 Wei Zhou. 保留所有权利。</p>
  </div>
</div>
  </div>
  <script src='/style/latest.js?config=TeX-MML-AM_CHTML'></script>
  <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
  <script src='/style/Valine.min.js'></script>
  <script src="https://apps.bdimg.com/libs/highlight.js/9.1.0/highlight.min.js"></script>
  <script type="text/javascript">
    hljs.initHighlightingOnLoad();
  </script>
  <script src="https://cdn.geogebra.org/apps/deployggb.js"></script>
  <script src="https://cdn1.lncld.net/static/js/2.5.0/av-min.js"></script>
  <script src='/style/readTimes.js'></script>
</body>
</html>
